package com.bjpowernode.money.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.bjpowernode.money.constant.IndexConstant;
import com.bjpowernode.money.mapper.BidInfoMapper;
import com.bjpowernode.money.mapper.FinanceAccountMapper;
import com.bjpowernode.money.mapper.LoanInfoMapper;
import com.bjpowernode.money.model.BidInfo;
import com.bjpowernode.money.model.LoanInfo;
import com.bjpowernode.money.model.PageModel;
import com.bjpowernode.money.service.BidInfoService;
import com.bjpowernode.money.utils.ReturnCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author ZHAOZHENYU
 * @since 2022/10/14
 */

@Service(interfaceClass = BidInfoService.class, version = "1.0.0", timeout = 20000)
@Component
public class BidInfoServiceImpl implements BidInfoService {

    @Autowired(required = false)
    private RedisTemplate redisTemplate;

    @Autowired(required = false)
    private BidInfoMapper bidInfoMapper;

    @Autowired
    private LoanInfoMapper loanInfoMapper;

    @Autowired
    private FinanceAccountMapper financeAccountMapper;

    @Override
    public Double queryBidMoneySum() {

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        Double bidMoneySum = (Double) redisTemplate.opsForValue().get(IndexConstant.BID_MONEY_SUM);
        if (bidMoneySum == null){
            synchronized (this) {
                // 再查一次是否已经放进redis
                bidMoneySum = (Double) redisTemplate.opsForValue().get(IndexConstant.BID_MONEY_SUM);
                if (bidMoneySum == null) {
                    bidMoneySum = bidInfoMapper.selectBidMoneySum();
                }
                redisTemplate.opsForValue().set(IndexConstant.BID_MONEY_SUM, bidMoneySum, 5, TimeUnit.MINUTES);
            }
        }
        return bidMoneySum;
    }


    @Override
    public List<BidInfo> queryBidInfoById(Integer loanId) {
        return bidInfoMapper.selectBidInfoById(loanId);
    }

    // TODO：改为分页
    @Override
    public List<BidInfo> queryBidInfoPage(Integer loanId, PageModel pageModel) {
        Long currentPage = pageModel.getCurrentPage();
        Integer pageSize = pageModel.getPageSize();
        // 起始查询下标
        Long start = (currentPage - 1) * pageSize;

        HashMap<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("loanId", loanId);
        paramsMap.put("start", start);
        paramsMap.put("content", pageSize);
        return bidInfoMapper.selectBidInfoPage(paramsMap);
    }

    @Override
    public Long queryBidInfoCount(Integer loanId) {
        return bidInfoMapper.selectBidInfoCount(loanId);
    }

    @Transactional      // 超卖问题  改为手动事务回滚让事务生效
    @Override
    public int invest(Integer loanId, Double bidMoney, Integer userId) {
        // 剩余可投金额减少
        // TODO：追加version乐观锁，解决并发数投资据不一致的问题，每次更新version+1
        int version = loanInfoMapper.selectVersionByPrimaryKey(loanId);
        int num = loanInfoMapper.updateLeftProductMoneyForInvest2(loanId,bidMoney,version);
        if (num == 0){
            // 手动回滚 让事务生效
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ReturnCode.BID_LOAN_MONEY_UPDATE_FAIL;
        }
        // 账户余额减少
        num = financeAccountMapper.updateAvailableMoneyForInvest(userId,bidMoney);
        if (num == 0){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ReturnCode.BID_FINANCE_MONEY_UPDATE_FAIL;
        }
        // 判断是否满标 改为满标
        LoanInfo loanInfo = loanInfoMapper.selectByPrimaryKey(loanId);
        if (loanInfo.getLeftProductMoney() == 0 && loanInfo.getProductStatus() == 0) {
            loanInfo.setProductStatus(1);
            loanInfo.setProductFullTime(new Date());
            num = loanInfoMapper.insertSelective(loanInfo);
            if (num == 0){
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ReturnCode.BID_LOAN_INFO_INSERT_FAIL;
            }
        }
        // 添加投资记录
        BidInfo bidInfo = new BidInfo();
        bidInfo.setLoanId(loanId);
        bidInfo.setBidMoney(bidMoney);
        bidInfo.setUid(userId);
        bidInfo.setBidTime(new Date());
        bidInfo.setBidStatus(1);
        num = bidInfoMapper.insertSelective(bidInfo);
        if (num == 0){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ReturnCode.BID_INFO_INSERT_FAIL;
        }
        return ReturnCode.BID_SUCCESS;
    }


    /**
     * TODO：模拟千人并发 加锁处理：同步 或者 数据库version
     */
    Map<Integer,LoanInfo> map = new HashMap<>();     // 存锁，放在这里对象唯一，map唯一
    @Transactional      // 超卖问题  改为手动事务回滚让事务生效
    @Override
    public int investConcurrency(Integer loanId, Double bidMoney, Integer userId) {

        // 判断是否有锁 没有锁就创建
        LoanInfo loanInfo_Lock = map.get(loanId);
        if (loanInfo_Lock == null){
            loanInfo_Lock = new LoanInfo();
            loanInfo_Lock.setId(loanId);
            map.put(loanId,loanInfo_Lock);
        }

        // 剩余可投金额减少  存在并发
        synchronized (loanInfo_Lock) {   // 拿到锁可以实现同一种产品被锁
            int num = loanInfoMapper.updateLeftProductMoneyForInvest(loanId,bidMoney);
            if (num == 0){
                // 手动回滚 让事务生效
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ReturnCode.BID_LOAN_MONEY_UPDATE_FAIL;
            }

            // 账户余额减少
            num = financeAccountMapper.updateAvailableMoneyForInvest(userId,bidMoney);
            if (num == 0){
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ReturnCode.BID_FINANCE_MONEY_UPDATE_FAIL;
            }

            // 判断是否满标 改为满标
            LoanInfo loanInfo = loanInfoMapper.selectByPrimaryKey(loanId);
            if (loanInfo.getLeftProductMoney() == 0 && loanInfo.getProductStatus() == 0) {
                loanInfo.setProductStatus(1);
                loanInfo.setProductFullTime(new Date());
                num = loanInfoMapper.updateByPrimaryKey(loanInfo);
                if (num == 0){
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return ReturnCode.BID_LOAN_INFO_INSERT_FAIL;
                }
            }

            // 添加投资记录
            BidInfo bidInfo = new BidInfo();
            bidInfo.setLoanId(loanId);
            bidInfo.setBidMoney(bidMoney);
            bidInfo.setUid(userId);
            bidInfo.setBidTime(new Date());
            bidInfo.setBidStatus(1);
            num = bidInfoMapper.insertSelective(bidInfo);
            if (num == 0){
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ReturnCode.BID_INFO_INSERT_FAIL;
            }
            return ReturnCode.BID_SUCCESS;

        }

    }

    @Override
    public List<BidInfo> queryAllBidInfo() {
        return bidInfoMapper.selectAllBidInfo();
    }

}
